﻿namespace WFBot.Framework.Web.Bootstrappers
{
    using System;
    using System.Collections.Generic;
    using System.Configuration;
    using System.Data.Common;
    using System.Linq;
    using Microsoft.Practices.Unity;
    using MvcExtensions;
    using MvcExtensions.Unity;
    using WFBot.Data;
    using WFBot.Framework.Infrastructure;
    using WFBot.Framework.Repositories;
    using WFBot.Framework.Services;
    using WFBot.Rtc;
    using WFBot.Workflow;

    public class RegisterServicesModule : IModule
    {
        private static readonly Func<LifetimeManager> transient = () => new TransientLifetimeManager();
        private static readonly Func<LifetimeManager> perRequest = () => new PerRequestLifetimeManager();
        private static readonly Func<LifetimeManager> singleton = () => new ContainerControlledLifetimeManager();

        public void Load(IUnityContainer container)
        {
            container.RegisterType<IUnitOfWork, UnitOfWork>(perRequest())
                     .RegisterType<IBotService, BotService>(perRequest())
                     .RegisterType<IOperatorService, OperatorService>(perRequest())
                     .RegisterType<ICommunicationService, RtcCommunicationService>(singleton())
                     .RegisterInstance(new PresenceUpdater());

            IBuildManager buildManager = container.Resolve<IBuildManager>();

            RegisterRepositories(buildManager, container);

            ConnectionStringSettings connectionStringSettings = ConfigurationManager.ConnectionStrings["WFBotDatabase"];
            string providerName = connectionStringSettings.ProviderName;

            DbProviderFactory databaseProviderFactory = DbProviderFactories.GetFactory(providerName);
            container.RegisterInstance(databaseProviderFactory);

            container.RegisterType<IDatabaseFactory, DatabaseFactory>(perRequest(), new InjectionConstructor(typeof(DbProviderFactory), connectionStringSettings.ConnectionString));

            var workflowService = new WorkflowService(container.Resolve<IBotService>());
            container.RegisterInstance<IWorkflowService>(workflowService);
        }

        private static void RegisterRepositories(IBuildManager buildManager, IUnityContainer container)
        {
            Type genericRepositoryType = typeof(IRepository<>);

            IEnumerable<Type> repositoryContractTypes = buildManager.PublicTypes.Where(type => (type != null) && type.IsInterface && type.GetInterfaces().Any(interfaceType => interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition().Equals(genericRepositoryType))).ToList();

            foreach (Type repositoryImplementationType in buildManager.ConcreteTypes.Where(implementationType => repositoryContractTypes.Any(contractType => contractType.IsAssignableFrom(implementationType))))
            {
                foreach (Type repositoryInterfaceType in repositoryImplementationType.GetInterfaces())
                {
                    container.RegisterType(repositoryInterfaceType, repositoryImplementationType, perRequest());
                }
            }
        }
    }
}
